package com.boat.starter.mockit.filter;

import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;

import com.boat.starter.mockit.annotation.MockMapping;
import com.boat.starter.mockit.props.MockitProperties;
import com.boat.starter.mockit.service.MockitCallService;
import com.boat.starter.mockit.service.MockitMatchService;

import lombok.extern.slf4j.Slf4j;

/**
 * mock统一拦截器
 * 
 * @author 周江
 *
 */
@Slf4j
@Aspect
public class MockFilter {

	private MockitProperties mockitProperties;
	private MockitMatchService mockitMatchService;
	private MockitCallService mockitCallService;

	public MockFilter(MockitProperties mockitProperties, MockitMatchService mockitMatchService,
			MockitCallService mockitCallService) {
		this.mockitProperties = mockitProperties;
		this.mockitMatchService = mockitMatchService;
		this.mockitCallService = mockitCallService;
		//log.info("初始化mockFilter完成");
	}

	private static final String EXECUTE_EXPR = "@annotation(com.boat.starter.mockit.annotation.MockMapping)";

	@Around(EXECUTE_EXPR)
	public Object doInvoke(ProceedingJoinPoint joinPoint) {
		if (isIllegalCall(joinPoint)) {
			throw new IllegalCallerException("禁止直接从外部调用mock方法");
		}

		MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
		Method method = methodSignature.getMethod();
		String path = getMockMappingPath(method);

		try {
			if (mockitMatchService.isMocked(path)) {
				String queryStr = mockitCallService.createQueryStr(method, joinPoint.getArgs());
				Object bodyParam = mockitCallService.getFirstDtoArg(joinPoint.getArgs());
				String uri = path + queryStr;
				String mockResult = mockitCallService.postMockReq(uri, bodyParam);
				return mockitCallService.buildMockResult(mockResult, method);
			}

			return joinPoint.proceed();
		} catch (Throwable ex) {
			if (ex instanceof RuntimeException) {
				throw (RuntimeException)ex;
			}
			throw new UndeclaredThrowableException(ex);
		}
	}

	private String getMockMappingPath(Method method) {
		try {
			MockMapping mockMapping = AnnotationUtils.getAnnotation(method, MockMapping.class);
			if (mockMapping != null && mockMapping.path() != null && mockMapping.path().length > 0) {
				return mockMapping.path()[0];
			}

			// 未正确设置mapping路径，视为不做mock
			return null;
		} catch (Exception ex) {
			throw new UndeclaredThrowableException(ex);
		}
	}
	
	/**
	 * 禁止直接从外部调用mock方法
	 * 
	 * @return
	 */
	private boolean isIllegalCall(ProceedingJoinPoint joinPoint) {
		String className = joinPoint.getTarget().getClass().getSimpleName();
		String methodName = joinPoint.getSignature().getName();

		StackTraceElement stack[] = Thread.currentThread().getStackTrace();
		for (int i = 0; i < stack.length; i++) {
			if (stack[i].getClassName().contains(className) && stack[i].getMethodName().equals(methodName)) {
				if (log.isDebugEnabled()) {
					log.debug("上一步方法：{}.{}", stack[i + 1].getClassName(), stack[i + 1].getMethodName());
				}
				// 如果上一步方法是：jdk.internal.reflect.NativeMethodAccessorImpl 。invoke0
				// 则视为没有经过内部业务类调用，而是直接从外部调用此方法的mock controller，这是禁止的。
				// mock方法发布成controller，只是为了生成swagger文档，用于导入到apifox中，不能直接调用
				if (stack[i + 1].getClassName().contains("jdk.internal.reflect")) {
					return true;
				}
			}
		}

		return false;
	}
}
